#ifndef     __FLAGS_H_INCLUDED__
#define     __FLAGS_H_INCLUDED__

#include    <cstdlib>
#include    <functional>
#include    <map>
#include    <string>
#include    <vector>

/**
 * Helper class to parse command-line arguments
 */
class Flags {
    typedef std::function<void(const std::string&)> Parser;

public:
    Flags(const std::string & title, const std::string & description)
        : _title(title + " - " + description)
        , _info()
        , _usage()
        , _parser()
        , _order() {
        _usage["h, help"] = "Display this message";
        _order.push_back("h, help");
    }

    /**
     * Set information for this application
     * 
     * \param key Information key
     * \param val Information description
     */
    inline void SetInfo(const std::string & key, const std::string & val) { _info[key] = val; }

    /**
     * Bind bool variable with key.
     * 
     * \param key Key to find from command line.
     * \param desc Description for display usage
     * \param val Variable to store parsed result.
     */
    inline void Bind(const std::string & key, const std::string & desc, bool & val) {
        __Bind(key, "[=yes|no]", desc, [&](const std::string & data) {
            val = data.empty() || data == "yes";
        });
    }

    /**
     * Bind integer variable with key
     * 
     * \param key Key to find from command line.
     * \param desc Description for display usage.
     * \param val Variable to store parsed result.
     */
    inline void Bind(const std::string & key, const std::string & desc, int & val) {
        __Bind(key, "=integer", desc, [&](const std::string & data) {
            val = atoi(data.empty() ? "0" : data.c_str());
        });
    }

    /**
     * Bind string variable with key
     * 
     * \param key Key to find from command line.
     * \param desc Description for display usage.
     * \param val Variable to store parsed result.
     */
    inline void Bind(const std::string & key, const std::string & desc, std::string & val) {
        __Bind(key, "=string", desc, [&](const std::string & data) {
            val = data;
        });
    }

    /**
     * Parse command line arguments
     * 
     * \param argc Argument count
     * \param argv Argument list
     */
    void Parse(int argc, char * argv[]);

private:
    void __Bind(const std::string & key, const std::string & format, const std::string & desc, Parser parser);
    void __Usage(const std::string & err);

private:
    std::string _title;
    std::map<std::string, std::string> _info;
    std::map<std::string, std::string> _usage;
    std::map<std::string, Parser> _parser;
    std::vector<std::string> _order;
};

#endif//!   __FLAGS_H_INCLUDED__